<?php declare(strict_types=1);

namespace Itdashu\SwoftThirdPartyService\Bean;

use Itdashu\SwoftThirdPartyService\Service\WeChat\Exceptions\InvalidResponseException;
use Swoft\Bean\Annotation\Mapping\Bean;

/**
 * Class WechatMini
 * @Bean(scope=Bean::PROTOTYPE)
 * @package Itdashu\SwoftThirdPartyService\Bean
 */
class WechatMini
{
  use \Swoft\Bean\Concern\PrototypeTrait;

  private $config;

  private $crypt;

  private $sessionKey = 'wechatMiniSession';

  public function initialize(array $config): void
  {
    $this->config = $config;
    $this->crypt = null;
  }

  /**
   * @return \Itdashu\SwoftThirdPartyService\Service\WeMini\Crypt
   */
  public function getCrypt(): \Itdashu\SwoftThirdPartyService\Service\WeMini\Crypt
  {
    if (!$this->crypt) {
      $this->crypt = new \Itdashu\SwoftThirdPartyService\Service\WeMini\Crypt($this->config);
    }
    return $this->crypt;
  }

  /**
   * 获取登录session
   * @param string $code
   * @return array
   * @throws \Itdashu\SwoftThirdPartyService\Service\WeChat\Exceptions\LocalCacheException
   */
  public function authSession(string $code): array
  {
    $output = $this->getCrypt()->session($code);
    if (!is_array($output) || !isset($output['session_key']) || !$output['session_key']) {
      throw new InvalidResponseException('Code 换取 SessionKey 失败', 403);
    }
    $data = [
      'sessionKey' => $output['session_key'],
      'openId' => $output['openid']
    ];
    \Itdashu\Token\Library\TokenHelper::token()->update($this->sessionKey, $data);
    return $data;
  }

  /**
   * 获取小程序实例缓存信息
   * @param string $name
   * @return mixed|null
   */
  public function getSession($name = '')
  {
    $output = \Itdashu\Token\Library\TokenHelper::token()->get($name);
    if (!$name) {
      return $output;
    }
    return $output[$name] ?? null;
  }

  /**
   * 数据解密
   * @param $iv
   * @param $encryptedData
   * @param string $code
   * @return mixed
   * @throws \Itdashu\SwoftThirdPartyService\Service\WeChat\Exceptions\InvalidDecryptException
   * @throws \Itdashu\SwoftThirdPartyService\Service\WeChat\Exceptions\LocalCacheException
   */
  public function decryptData($iv, $encryptedData, $code = '')
  {
    if ($code) {
      $this->authSession($code);
    }

    $output = $this->getCrypt()->decode($iv, $this->getSession('sessionKey'), $encryptedData);

    if (!$output) {
      throw new \Itdashu\SwoftThirdPartyService\Service\WeChat\Exceptions\InvalidDecryptException('数据解密失败');
    }

    return $output;
  }

  /**
   * 返回克隆实例。
   * @param array $config
   * @return WechatMini
   */
  public static function new(array $config = []): self
  {
    $self = self::__instance();
    $self->initialize($config);

    return $self;
  }
}
